#pragma once
/*
服务器数据管理模块:
功能:管理服务器备份文件的基本信息;
使用一个backupFileMsg类来描述一个备份文件的基本信息:
描述的基本属性有:
1. 备份文件名
2. 备份文件的大小
3. 备份文件的最近访问时间
4. 备份文件的最近修改时间
5. 备份文件的真实存储路径
6. 当前备份文件是否被压缩
7. 备份文件被压缩后的存储路径
8. 当前备份文件的资源下载请求URL
构造的时候:根据备份文件的真实存储路径来完成初始化

然后利用blackupManger类来组织管理备份文件结构体

含有以下属性:
1. hash:用来组织备份文件的数据结构 <url,backupFileMsg>
2. shared_mutex; 读写锁来保证多线程情况下的线程安全和读-读线程的并发性能(C++17提供:不直接使用:pthread_rwlock_t是为了保证程序的可移植性)
3. 持久化路径:服务端需要将管理起来的数据备份到对应的文件中，以便于持久化存储
提供以下接口:
构造时:将已经备份的文件信息从文件中读取出来
0. storage:持久化接口
1. insert;提供一个backupFileMsg对象就能将其插入
2. update:提供一个已经修改过的backupFileMsg对象再将其插入
3. getBackupFileMsgByURL:通过一个文件请求路径来获得一个backupFileMsg对象
4. getBackupFileMsgByRealPath:通过备份文件的真实路径来获取一个backupFileMsg对象(提供这个接口的目的是为了避免:文件是真备份了，但是备份信息丢失了的情况，
如果我们通过该接口获取备份文件结构体失败了的话，那么说明我们的数据丢失，我们要将其进行重修构建备份信息结构体)
5. getAll:获取所有文件的备份信息

*/
#include <shared_mutex>
#include <unordered_map>
#include <mysql/mysql.h>
#include "util.hpp"
#include "config.hpp"
namespace Cloud
{
    struct blackupFileMsg
    {
        blackupFileMsg() = default;
        std::string _filename;
        int64_t _fsize;
        time_t _aTime;
        time_t _mTime;
        std::string _realPath;
        bool _compressFlag;
        std::string _compressPath;
        std::string _url;
        blackupFileMsg(const std::string &realpath,const std::string &comPath);
    };

    class MysqlManger
    {
    public:
        // 1.完成初始化工作
        MysqlManger();
        // 2. 完成连接工作
        bool connect(const std::string &user, const std::string &password, const std::string &db, const std::string &host = "localhost", uint16_t port = 3306);
        // 3. 下达SQL语句
        bool query(const std::string &sql);
        // 4.获取列数
        size_t col(MYSQL_RES *result);
        // 5. 获取行数
        size_t row(MYSQL_RES *result);
        // 6. 获取查询结果
        bool result(MYSQL_RES **result);
        // 7. 获取列属性
        MYSQL_FIELD *numFields(MYSQL_RES *result);
        // 8. 获取每一行结果
        MYSQL_ROW fetchRow(MYSQL_RES *result);
        // 9. 释放资源
        ~MysqlManger();

    private:
        MYSQL _mysql;
    };
    class DataManger
    {
    public:
        bool insert(const blackupFileMsg &e);
        bool update(const blackupFileMsg &e);
        bool getBackupFileMsgByURL(const std::string &url, blackupFileMsg *buffer);
        bool getBackupFileMsgByRealPath(const std::string &realPath, blackupFileMsg *buffer);
        bool getAll(std::vector<blackupFileMsg> *buffer);
        DataManger(const std::string &tableName);

    private:
        std::string _tableName;
        std::unordered_map<std::string, Cloud::blackupFileMsg> _urlTable;
        std::unordered_map<std::string, Cloud::blackupFileMsg> _realPathTable;
        std::shared_mutex _shmux;
        MysqlManger _mysql;
    };
    DataManger::DataManger(const std::string &tableName) : _tableName(tableName), _shmux(), _mysql()
    {
        std::string username(Cloud::Config::getInstance().userName());
        std::string password(Cloud::Config::getInstance().password());
        std::string database(Cloud::Config::getInstance().database());
        std::string host(Cloud::Config::getInstance().host());
        uint16_t port = (Cloud::Config::getInstance().port());

        if (!_mysql.connect(username, password, database, host, port))
            exit(-1);
        //先判断表是否存在,不存在则创建
        std::string sql = "create table if not exists ";
        sql += tableName + "(";
        sql += "filename varchar(100) not null,";
        sql += "filesize bigint(20) unsigned not null,";
        sql += "accessTime bigint(20) unsigned not null,";
        sql += "modifyTime bigint(20) unsigned not null,";
        sql += "realPath varchar(100) not null,";
        sql += "compressFlag tinyint  not null,";
        sql += "compressPath varchar(100) not null,";
        sql += "url varchar(100) primary key not null,index(filename))";
        if(!_mysql.query(sql))
        {
            std::cout<<__LINE__<<"SQL语句执行失败"<<std::endl;
            std::cout<<sql<<std::endl;
            exit(-1);
        }

        MYSQL_RES *res;
        sql="select * from ";
        sql+=_tableName;
       _mysql.query(sql);
        _mysql.result(&res);
        if(res==nullptr) return;
        MYSQL_ROW row;
        size_t col = _mysql.col(res);
        while ((row = _mysql.fetchRow(res)) != nullptr)
        {
            blackupFileMsg b1;
            int i = 0;
            b1._filename = row[i++];
            b1._fsize = stoul(std::string(row[i++]));
            b1._aTime = stoul(std::string(row[i++]));
            b1._mTime = stoul(std::string(row[i++]));
            b1._realPath = row[i++];
            b1._compressFlag = (std::string(row[i++]) == "1" ? true : false);
            b1._compressPath = row[i++];
            b1._url = row[i++];
            _urlTable[b1._url] = b1;
            _realPathTable[b1._realPath]=b1;
        std::cout<<__FILE__<<__LINE__<<b1._compressFlag<<std::endl;
        }
        mysql_free_result(res);
    }
    bool DataManger ::insert(const blackupFileMsg &e)
    {
        _shmux.lock();
        _urlTable [e._url] = e;
        _realPathTable [e._realPath] = e;
        // 将数据插入sql中
        std::string sql = "insert into ";
        sql += _tableName;
        sql += " values(";
        std::string sep = "'";
        sql += sep + e._filename + sep + ',';
        sql += sep + std::to_string(e._fsize) + sep + ',';
        sql += sep + std::to_string(e._aTime) + sep + ',';
        sql += sep + std::to_string(e._mTime) + sep + ',';
        sql += sep + e._realPath + sep + ',';
        sql += sep + (e._compressFlag ? "1" : "0") + sep + ',';
        sql += sep + e._compressPath + sep + ',';
        sql += sep + e._url + sep;
        sql += ')';
        bool ret = _mysql.query(sql);
        _shmux.unlock();
        return ret;
    }
    bool DataManger::update(const blackupFileMsg &e)
    {
        _shmux.lock();
         _urlTable [e._url] = e;
        _realPathTable [e._realPath] = e;
        // 同时更新数据库中的信息
        std::string sql = "update ";
        std::string sep = "'";
        sql += _tableName;
        sql += " set ";
        sql += std::string("filename") + "=" + sep + e._filename + sep + ",";
        sql += std::string("filesize") + "=" + std::to_string(e._fsize) + ",";
        sql += std::string("accessTime") + "=" + std::to_string(e._aTime) + ",";
        sql += std::string("modifyTime") + "=" + std::to_string(e._mTime) + ",";
        sql += std::string("realPath") + "=" + sep + e._realPath + sep + ",";
        sql += std::string("compressFlag") + "=" + (e._compressFlag ? "1" : "0") + ",";
        sql += std::string("compressPath") + "=" + sep + e._compressPath + sep;
        sql += std::string("where url = ") + sep + e._url + sep;
        bool ret = _mysql.query(sql);
        _shmux.unlock();
        return ret;
    }
    bool DataManger::getBackupFileMsgByURL(const std::string &url, blackupFileMsg *buffer)
    {
        _shmux.lock_shared();
        if (!_urlTable.count(url))
        {
            _shmux.unlock_shared();
            return false;
        }
        *buffer = _urlTable[url];
        std::cout<<__FILE__<<__LINE__<<buffer->_compressFlag<<std::endl;
        _shmux.unlock_shared();
        return true;
    }
    bool DataManger::getBackupFileMsgByRealPath(const std::string &realPath, blackupFileMsg *buffer)
    {
         _shmux.lock_shared();
        if (!_realPathTable.count(realPath))
        {
            _shmux.unlock_shared();
            return false;
        }
        *buffer = _realPathTable[realPath];
        _shmux.unlock_shared();
        return true;
    }
    bool DataManger::getAll(std::vector<blackupFileMsg> *buffer)
    {
        _shmux.lock_shared();
        buffer->reserve(_urlTable.size());
        for (auto &e : _urlTable)
            buffer->push_back(e.second);
        _shmux.unlock_shared();
        return true;
    }
    blackupFileMsg::blackupFileMsg(const std::string &realpath,const std::string &comPath) : _realPath(realpath),_compressPath(comPath)
    {
        fileUtil f1(_realPath);
        _filename = f1.filename();
        _fsize = f1.fileSize();
        _aTime = f1.getAccessTime();
        _mTime = f1.getModifyTime();
        _compressFlag = false;
        // 压缩存储地址=压缩前缀路径+filename+压缩后缀
        auto &tmp = Config::getInstance();
        int pos1 = _realPath.find_last_of("/");
        int pos2 = _realPath.find_last_of("/", pos1 - 1);
        // url资源请求下载路径=下载路路径前缀+文件名
        _url = tmp.urlPrefix() + _filename;
    }
    MysqlManger::MysqlManger()
    {
        if (NULL == mysql_init(&_mysql))
            exit(-1);
    }
    bool MysqlManger::connect(const std::string &user, const std::string &password, const std::string &db, const std::string &host, uint16_t port)
    {
        // 这一步关键，必须要，不要数据库会连接失败!
        mysql_options(&_mysql, MYSQL_READ_DEFAULT_GROUP, "main");

        if (nullptr == mysql_real_connect(&_mysql, host.c_str(), user.c_str(), password.c_str(), db.c_str(), port, nullptr, CLIENT_MULTI_STATEMENTS))
        {
            std::cout << "mysql 连接失败 begin" << std::endl;
            return false;
        }
        std::cout << "mysql 连接成功" << std::endl;
        return true;
    }
    bool MysqlManger::query(const std::string &sql)
    {
        return mysql_query(&_mysql, sql.c_str()) == 0;
    }
    size_t MysqlManger::col(MYSQL_RES *_result)
    {
        return mysql_num_fields(_result);
    }
    size_t MysqlManger::row(MYSQL_RES *_result)
    {
        return mysql_num_rows(_result);
    }
    MYSQL_FIELD *MysqlManger::numFields(MYSQL_RES *_result)
    {
        return mysql_fetch_fields(_result);
    }
    MYSQL_ROW MysqlManger::fetchRow(MYSQL_RES *_result)
    {
        return mysql_fetch_row(_result);
    }
    bool MysqlManger::result(MYSQL_RES **result)
    {
        MYSQL_RES *res = mysql_use_result(&_mysql);
        if (res == nullptr)
            return false;
        *result = res;
        return true;
    }
    MysqlManger::~MysqlManger()
    {
        mysql_close(&_mysql);
    }

}